package com.tanhua.server.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.PageUtil;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.github.tobato.fastdfs.domain.conn.FdfsWebServer;
import com.github.tobato.fastdfs.domain.fdfs.StorePath;
import com.github.tobato.fastdfs.service.FastFileStorageClient;
import com.tanhua.autoconfig.template.OssTemplate;
import com.tanhua.commons.constants.Constants;
import com.tanhua.commons.constants.LogKey;
import com.tanhua.commons.constants.LogType;
import com.tanhua.dubbo.api.CommentApi;
import com.tanhua.dubbo.api.UserInfoApi;
import com.tanhua.dubbo.api.VideoApi;
import com.tanhua.model.domain.UserInfo;
import com.tanhua.model.enums.CommentSubject;
import com.tanhua.model.enums.CommentType;
import com.tanhua.model.mongo.Comment;
import com.tanhua.model.mongo.FocusUser;
import com.tanhua.model.mongo.Video;
import com.tanhua.model.vo.CommentVO;
import com.tanhua.model.vo.ErrorResult;
import com.tanhua.model.vo.PageResult;
import com.tanhua.model.vo.VideoVO;
import com.tanhua.server.exception.BusinessException;
import com.tanhua.server.interceptor.UserHolder;
import org.apache.commons.lang.StringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author Xiao
 * @version 1.0
 * @description
 * @date 2021/11/2
 */
@Service
public class SmallVideosService {
    @DubboReference
    private VideoApi videoApi;
    @Autowired
    private FastFileStorageClient fastFileStorageClient;
    @Autowired
    private FdfsWebServer fdfsWebServer;
    @Autowired
    private OssTemplate ossTemplate;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @DubboReference
    private UserInfoApi userInfoApi;
    @Autowired
    private MovementsService movementsService;
    @DubboReference
    private CommentApi commentApi;
    @Autowired
    private CommentsService commentsService;
    @Autowired
    private MqMessageService mqMessageService;

    /**
     * 小视频-视频上传
     * @param videoThumbnail 视频封面文件
     * @param videoFile 视频文件
     */
    public void uploadVideo(MultipartFile videoThumbnail, MultipartFile videoFile) throws IOException {
        if (videoThumbnail.isEmpty() || videoFile.isEmpty()) {
            throw new BusinessException(ErrorResult.error());
        }
        //将视频上传到FastDFS，获取访问路径
        String filename = videoFile.getOriginalFilename();  //abc.mp4
        filename = filename.substring(filename.lastIndexOf(".") + 1);
        StorePath storePath = fastFileStorageClient.uploadFile(
                videoFile.getInputStream(), videoFile.getSize(), filename, null);
        String videoUrl = fdfsWebServer.getWebServerUrl() + storePath.getFullPath();
        //将封面视频上传到阿里云OSS，获取访问路径
        String picUrl = ossTemplate.upload(
                videoThumbnail.getOriginalFilename(), videoThumbnail.getInputStream());
        //构建Video对象
        Video video = new Video();
        video.setUserId(UserHolder.getUserId());
        video.setText("愿你像阳光，明媚不忧伤！");
        video.setPicUrl(picUrl);
        video.setVideoUrl(videoUrl);
        //调用API保存数据
        String videoId = videoApi.saveVideo(video);
        if (StringUtils.isEmpty(videoId)) {
            throw new BusinessException(ErrorResult.error());
        }
        //发送日志
        mqMessageService.sendLogMessage(
                UserHolder.getUserId(), LogType.VIDEO_PUBLISH, LogKey.VIDEO, videoId);
    }

    /**
     * 小视频-列表查询
     * @param page
     * @param pagesize
     * @return
     */
    @Cacheable(
            value = "videos",
            key = "T(com.tanhua.server.interceptor.UserHolder).getUserId() + '_' + #page + '_' + #pagesize")
    //videos::userId_page_pagesize
    public PageResult getVideos(Integer page, Integer pagesize) {
        //从redis中查询视频数据
        Long userId = UserHolder.getUserId();
        String redisKey = Constants.VIDEOS_RECOMMEND + userId.toString();
        String redisValue = redisTemplate.opsForValue().get(redisKey);

        List<Video> videos = new ArrayList<>();
        Integer redisPages = 0;     //redis中的数据总页数
        Integer followCount = 0;    //断层页需要补充的条数

        //判断数据是否存在
        if (StringUtils.isNotEmpty(redisValue)) {
            //给redisPages和followCount赋值
            String[] values = redisValue.split(",");
            int totalCount = values.length;
            // PageUtil.totalPage(totalCount, pageSize) -->
            // totalCount % pageSize == 0 ? totalCount / pageSize : totalCount / pageSize + 1;
            redisPages = PageUtil.totalPage(totalCount, pagesize);
            followCount = pagesize - (totalCount % pagesize);

            if (page < redisPages) {
                Long skip = (long) (page - 1) * pagesize;
                List<Long> vids = Arrays.stream(values)
                        .skip(skip)
                        .limit(pagesize)
                        //.map(item -> Long.valueOf(item))
                        .map(Long::valueOf)
                        .collect(Collectors.toList());
                videos = videoApi.findByVids(vids);
                //判断是否是断层页，如果是断层页，断层页需要补充的条数需要从数据库查询后补充进来
                if (videos.size() < pagesize) {
                    List<Video> followVideos = videoApi.getFollowVideos(userId, followCount);
                    videos.addAll(followVideos);
                }
            }
        }
        //如果redis没有查询到数据，直接从数据库获取相应数据
        if (CollUtil.isEmpty(videos)) {
            //page - redisPages：数据库查询的当前页码(传入页码数-redis数据的总页数)
            //如redis中有2页数据，传入页码数为3，此次请求redis中没有第3页数据，则从数据库查询并返回第1页结果
            //followCount：需要额外跳过补充至断层页的数据
            page = page - redisPages;
            videos = videoApi.getVideos(userId, page, pagesize, followCount);
        }
        if (CollUtil.isEmpty(videos)) {
            return new PageResult();
        }
        List<Long> userIds = CollUtil.getFieldValues(videos, "userId", Long.class);
        Map<Long, UserInfo> infoMap = userInfoApi.selectByIdsAndCondition(userIds, null);
        List<VideoVO> videoVOs = new ArrayList<>();
        for (Video video : videos) {
            UserInfo userInfo = infoMap.get(video.getUserId());
            if (ObjectUtil.isNotEmpty(userInfo)) {
                VideoVO videoVO = VideoVO.init(userInfo, video);
                //设置是否关注
                String key = Constants.VIDEOS_INTERACT_KEY + video.getUserId();
                String hashKey = Constants.VIDEO_FOCUS_HASHKEY + userId;
                Boolean hasKey = redisTemplate.opsForHash().hasKey(key, hashKey);
                if (hasKey) {
                    videoVO.setHasFocus(1);
                }
                //设置是否喜欢
                key = Constants.MOVEMENTS_INTERACT_KEY + video.getId();
                hashKey = Constants.MOVEMENT_LIKE_HASHKEY + userId;
                hasKey = redisTemplate.opsForHash().hasKey(key, hashKey);
                if (hasKey) {
                    videoVO.setHasLiked(1);
                }

                videoVOs.add(videoVO);
            }
        }
        return new PageResult(page, pagesize, 0, videoVOs);
    }

    /**
     * 小视频-用户关注/取消关注
     * @param followUserId  要(取消)关注的用户id
     * @param choose        true(关注)/false(取消关注)
     */
    public void focus(Long followUserId, Boolean choose) {
        Long userId = UserHolder.getUserId();
        Boolean hasFocused = videoApi.hasFocused(userId, followUserId);
        if (hasFocused && choose) {
            throw new BusinessException(ErrorResult.focusError());
        }
        if (!hasFocused && !choose) {
            throw new BusinessException(ErrorResult.unFocusError());
        }
        String key = Constants.VIDEOS_INTERACT_KEY + followUserId;
        String hashKey = Constants.VIDEO_FOCUS_HASHKEY + userId;
        if (choose ) {
            FocusUser focusUser = new FocusUser();
            focusUser.setUserId(userId);
            focusUser.setFollowUserId(followUserId);
            focusUser.setCreated(System.currentTimeMillis());
            videoApi.saveFocusUser(focusUser);
            redisTemplate.opsForHash().put(key, hashKey, "1");
        } else {
            videoApi.removeFocusUser(userId, followUserId);
            redisTemplate.opsForHash().delete(key, hashKey);
        }
    }

    /**
     * 对视频(取消)点赞
     * @param id        要(取消)点赞的视频id
     * @param choose    true(点赞)/false(取消点赞)
     */
    public void like(String id, Boolean choose) {
        movementsService.disposeComment(id, choose, CommentType.LIKE, CommentSubject.VIDEO);
    }

    /**
     * 小视频-评论列表
     * @param id 视频id
     * @param page
     * @param pagesize
     * @return
     */
    public PageResult getComments(String id, Integer page, Integer pagesize) {
        List<Comment> comments = commentApi.selectByMovementId(id, CommentType.COMMENT, page, pagesize);
        return commentsService.getPageResult(page, pagesize, comments);
    }

    /**
     * 小视频-发布评论
     * @param id        视频id
     * @param content   评论正文
     */
    public void publishComment(String id, String content) {
        commentsService.publish(id, content, CommentSubject.VIDEO);
    }

    /**
     * 小视频-评论点赞/取消点赞
     * @param id        评论id
     * @param choose    true(点赞)/false(取消点赞)
     */
    public void commentsLike(String id, Boolean choose) {
        commentsService.disposeComment(id, choose, CommentSubject.COMMENT);
    }
}
